package com.dsteiner.widgets.server;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.dsteiner.widgets.client.StringUtil;
import com.dsteiner.widgets.client.SuggestBoxWidget.Suggestion;
import com.dsteiner.widgets.client.SuggestBoxWidget.SuggestionRequest;
import com.dsteiner.widgets.client.SuggestBoxWidget.SuggestionResult;
import com.dsteiner.widgets.client.domain.Distinct;
import com.dsteiner.widgets.client.domain.ModelObject;
import com.dsteiner.widgets.client.domain.SearchableProperty;
import com.dsteiner.widgets.client.event.DataUpdateEvent;
import com.dsteiner.widgets.client.event.EventListener;
import com.dsteiner.widgets.client.event.EventManagerImpl;
import com.dsteiner.widgets.client.rpc.BaseDataService;
import com.dsteiner.widgets.client.rpc.CrudResult;
import com.dsteiner.widgets.client.rpc.SearchRequest;
import com.dsteiner.widgets.client.rpc.SearchResult;
import com.dsteiner.widgets.client.rpc.SearchRequest.SearchRequestImpl;
import com.dsteiner.widgets.server.dao.BaseDataDao;
import com.dsteiner.widgets.server.dao.Change;
import com.dsteiner.widgets.server.dao.Parameter;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

public class BaseDataServiceImpl extends RemoteServiceServlet implements BaseDataService{

	private BaseDataDao dataDao;
	private DistinctServiceImpl distinctService = new DistinctServiceImpl();
	
	public CrudResult createModelObject(ModelObject modelObject) {
		ModelObject savedObj = dataDao.save(modelObject);
		CrudResult crudResult = new CrudResult();
		crudResult.setModelObject(savedObj);
		
		List<Distinct> distinct = distinctService.createDistinctsFromAnnotations(savedObj);
		if(distinct != null){
			for (Distinct distinct2 : distinct) {
				distinctService.addDistinct(distinct2);
			}
		}
		return crudResult;
	}

	public CrudResult deleteObject(ModelObject modelObject) {
		dataDao.deleteObject(modelObject);
		CrudResult crudResult = new CrudResult();
		crudResult.setModelObject(modelObject);
		
		List<Distinct> distinct = distinctService.createDistinctsFromAnnotations(modelObject);
		if(distinct != null){
			for (Distinct distinct2 : distinct) {
				distinctService.removeDistinct(distinct2);
			}
		}
		
		return crudResult;
	}

	public CrudResult getObjectById(String id, String objectClassName) {
		CrudResult crudResult = new CrudResult();
		try{
			Class clazz = Class.forName(objectClassName);
			ModelObject modelObject = (ModelObject) dataDao.getObjectById(id, clazz);
			crudResult.setModelObject(modelObject);
		}catch(Exception ex){
			ex.printStackTrace();
		}
		return crudResult;
	}
	
	public SuggestionResult getSuggestions(SuggestionRequest request) {
		SearchRequestImpl searchRequest = new SearchRequestImpl();
		String text = request.getText();
		searchRequest.setText(text);
		searchRequest.setClassName(request.getClassName());
		searchRequest.setOffset(0);
		searchRequest.setLimit(request.getDisplayCount());
		searchRequest.setType(SearchRequest.TYPE_PREFIX);
		
		SearchResult<ModelObject> searchResult = search(searchRequest);
		SuggestionResult suggestionResult = new SuggestionResult();
		List<Suggestion> suggestions = new ArrayList<Suggestion>();
		for (Object object : searchResult.getObjects()) {
			String suggestionText = getSuggestionText(text, object, SearchableProperty.class);
			if(suggestionText == null)
				continue;
			Suggestion suggestion = new Suggestion(suggestionText);
			if(!suggestions.contains(suggestion))
				suggestions.add(suggestion);
		}
		suggestionResult.setSuggestions(suggestions);
		
		return suggestionResult;
	}

	public <T extends ModelObject> SearchResult<T> search(SearchRequest searchRequest) {
		SearchResult<T> searchResult = dataDao.search(searchRequest);
		return searchResult;
	}

	public void setDataDao(BaseDataDao dataDao) {
		this.dataDao = dataDao;
	}

	public CrudResult updateObject(ModelObject modelObject) {
		Map<String, Change> changes = dataDao.updateObject(modelObject);
		CrudResult crudResult = new CrudResult();
		crudResult.setModelObject(modelObject);
		List<Distinct> distincts = distinctService.createDistinctsFromAnnotations(modelObject);
		if(distincts != null){
			for (Distinct distinct : distincts) {
				if(changes.containsKey(distinct.getProperty())){
					Change change = changes.get(distinct.getProperty());
					distinct.setValue(change.getOldValue());
					distinctService.removeDistinct(distinct);
					distinct.setValue(change.getValue());
					distinctService.addDistinct(distinct);
				}
			}
		}
		return crudResult;
	}

	private String getSuggestionText(String text, Object object, Class annotationClass) {
		try {
			Set<Field> fields = ServerUtil.getAnnotationFields(object.getClass(), annotationClass);
			Method[] methods = object.getClass().getMethods();
			for (Field field : fields) {
				String methodName = "get"+StringUtil.toUpperCaseFirstChar(field.getName());
				Object value = object.getClass().getMethod(methodName, null).invoke(object, null);
				if(value instanceof String && (((String)value).toLowerCase().startsWith(text.toLowerCase()))){
					return (String) value;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
}
